﻿using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Linq.Reactive;
using System.Text;
using MathService;
using MathService.Values;

namespace System
{
    /// <summary>Класс методов расширений для интерфейса перечисления</summary>
    public static class IEnumerableExtentions
    {
        public static string Aggregate(this IEnumerable<string> Lines)
        {
            return Lines.Aggregate(new StringBuilder(), (SB, s) => SB.AppendLine(s), SB => SB.ToString());
        }

        public static void AddToDictionary<TKey, TValue>(this IEnumerable<TValue> collection, IDictionary<TKey, TValue> dictionary, Func<TValue, TKey> converter)
        {
            Contract.Requires(dictionary != null);
            Contract.Requires(converter != null);
            if(collection == null) return;
            foreach(var value in collection)
                dictionary.Add(converter(value), value);
        }

        public static IEnumerable<Q> ConvertToType<T, Q>(this IEnumerable<T> collection)
        {
            var target_type = typeof(Q);
            var source_type = typeof(T);

            if(source_type == target_type) return (IEnumerable<Q>)collection;
            if(collection == null) return null;


            Func<object, object> type_converter = null;
            Func<T, Q> converter = source_type == typeof(object)
                        ? (o => (Q)target_type.Cast(o))
                        : (Func<T, Q>)(o => (Q)(type_converter ?? (type_converter = target_type.GetConverterFrom(source_type)))(o));
            return collection.Select(converter);
        }

        public static IEnumerable<Q> SelectWithLastValue<T, Q>(this IEnumerable<T> collection, Func<T, T, Q> Converter)
        {
            Contract.Requires(collection != null);
            Contract.Requires(Converter != null);
            var first = true;
            var last = default(T);
            foreach(var item in collection)
            {
                if(first)
                {
                    last = item;
                    first = false;
                    continue;
                }
                yield return Converter(last, item);
                last = item;
            }
        }

        public static IEnumerable<T> AtFirst<T>(this IEnumerable<T> collection, Action<T> action)
        {
            Contract.Requires(collection != null);
            Contract.Requires(action != null);
            var first = true;
            foreach(var item in collection)
            {
                if(first)
                {
                    action(item);
                    first = false;
                }
                yield return item;
            }
        }

        public static IEnumerable<T> AtLast<T>(this IEnumerable<T> collection, Action<T> action)
        {
            Contract.Requires(collection != null);
            Contract.Requires(action != null);
            var last = default(T);
            var any = false;
            foreach(var item in collection)
            {
                any = true;
                last = item;
                yield return last;
            }
            if(any) action(last);
        }

        public static IEnumerable<T> Before<T>(this IEnumerable<T> collection, Action ActionBefore)
        {
            Contract.Requires(collection != null);
            Contract.Requires(ActionBefore != null);
            ActionBefore();
            foreach(var item in collection) yield return item;
        }

        public static IEnumerable<T> OnComplite<T>(this IEnumerable<T> collection, Action CompliteAction)
        {
            Contract.Requires(collection != null);
            Contract.Requires(CompliteAction != null);
            foreach(var item in collection)
                yield return item;
            CompliteAction();
        }

        public class EnumerableHystory<T> : IEnumerable<T>, IObservableEx<T>
        {
            private int f_HystoryLength;
            private readonly List<T> f_Queue;
            private readonly IObserverEx<T> f_Observer = new SimpleObserverEx<T>();

            public T Current { get; private set; }

            public int Length { get { return f_HystoryLength; } set { f_HystoryLength = value; Check(); } }
            public int Count { get { return f_Queue.Count; } }

            public T this[int i]
            {
                get
                {
                    Contract.Requires(i >= 0);
                    Contract.Requires(i < Count);
                    return f_Queue[f_Queue.Count - i];
                }
            }

            public EnumerableHystory(int HystoryLength)
            {
                Contract.Requires(HystoryLength >= 0);
                Contract.Ensures(f_Queue != null);
                Contract.Ensures(f_HystoryLength == HystoryLength);
                f_HystoryLength = HystoryLength;
                f_Queue = new List<T>(HystoryLength);
            }

            private void Check()
            {
                Contract.Ensures(Count <= Length);
                while(f_Queue.Count > f_HystoryLength) f_Queue.RemoveAt(0);
            }

            public void Add(T item)
            {
                Contract.Ensures(Count == Contract.OldValue(Count) + 1 || Count == Length);
                Contract.Ensures(Count <= Length);
                f_Queue.Add(item);
                Current = item;
                f_Observer.OnNext(item);
                Check();
            }

            public IEnumerator<T> GetEnumerator() { return f_Queue.GetEnumerator(); }

            IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }

            public IObserverEx<T> GetObserver() { return f_Observer; }
        }

        public static IEnumerable<TOut> SelectWithHystory<TIn, TOut>(this IEnumerable<TIn> collection,
            Func<EnumerableHystory<TIn>, TOut> Selector, int HystoryLength)
        {
            Contract.Requires(collection != null);
            var hystory = new EnumerableHystory<TIn>(HystoryLength);
            foreach(var item in collection)
            {
                hystory.Add(item);
                yield return Selector(hystory);
            }
        }

        public static StatisticValue GetStatistic(this IEnumerable<double> collection, int Length = 0)
        {
            Contract.Requires(collection != null);
            Contract.Requires(Length >= 0);
            if(Length > 0)
                return new StatisticValue(Length).InitializeObject(sv => sv.AddEnumerable(collection));
            var values = collection.ToArray();
            var result = new StatisticValue(values.Length);
            result.AddEnumerable(values);
            return result;
        }

        public static IEnumerable<double> FilterNullValuesFromEnd(this IEnumerable<double> collection)
        {
            Contract.Requires(collection != null);
            var n = 0;
            foreach(var value in collection)
            {
                if(value.Equals(0)) n++;
                else
                {
                    for(; n > 0; n--) yield return 0;
                    yield return value;
                }
            }
        }

        //public static IEnumerable<T> InitializeLazy<T>(this T collection, Action<T> Initializer) where T : IEnumerable<T>
        //{
        //    foreach(var value in collection)
        //    {
        //        Initializer(collection);
        //        yield return value;
        //    }
        //}

        public static void GetMinMax<T>(this IEnumerable<T> collection, Func<T, double> selector, out T Min, out T Max)
        {
            Contract.Requires(!ReferenceEquals(collection, null));
            Contract.Requires(!ReferenceEquals(selector, null));

            var min = new MinValue();
            var max = new MaxValue();
            Min = default(T);
            Max = default(T);
            foreach(var value in collection)
            {
                var f = selector(value);
                if(min.AddValue(f)) Min = value;
                if(max.AddValue(f)) Max = value;
            }
        }

        public static void GetMinMax<T>(this IEnumerable<T> collection, Func<T, double> selector,
            out T Min, out int MinIndex, out T Max, out int MaxIndex)
        {
            Contract.Requires(!ReferenceEquals(collection, null));
            Contract.Requires(!ReferenceEquals(selector, null));

            var min = new MinValue();
            var max = new MaxValue();
            Min = default(T);
            Max = default(T);
            MinIndex = MaxIndex = -1;
            foreach(var value in collection.Select((v, i) => new { v, i }))
            {
                var f = selector(value.v);
                if(min.AddValue(f))
                {
                    Min = value.v;
                    MinIndex = value.i;
                }
                if(max.AddValue(f))
                {
                    Max = value.v;
                    MaxIndex = value.i;
                }
            }
        }

        public static T GetMax<T>(this IEnumerable<T> collection, Func<T, double> selector)
        {
            Contract.Requires(!ReferenceEquals(collection, null));
            Contract.Requires(!ReferenceEquals(selector, null));

            var max = new MaxValue();
            var result = default(T);
            collection.Where(t => max.AddValue(selector(t))).Foreach(v => result = v);
            return result;
        }

        public static T GetMax<T>(this IEnumerable<T> collection, Func<T, double> selector, out int index)
        {
            Contract.Requires(!ReferenceEquals(collection, null));
            Contract.Requires(!ReferenceEquals(selector, null));

            var max = new MaxValue();
            var result = default(T);
            var I = -1;
            collection.Select((t, i) => new { t, i })
                .Where(v => max.AddValue(selector(v.t)))
                .Foreach(v => { result = v.t; I = v.i; });
            index = I;
            return result;
        }

        public static T GetMin<T>(this IEnumerable<T> collection, Func<T, double> selector)
        {
            Contract.Requires(!ReferenceEquals(collection, null));
            Contract.Requires(!ReferenceEquals(selector, null));

            var min = new MinValue();
            var result = default(T);
            collection.Where(t => min.AddValue(selector(t))).Foreach(v => result = v);
            return result;
        }
        public static T GetMin<T>(this IEnumerable<T> collection, Func<T, double> selector, out int index)
        {
            Contract.Requires(!ReferenceEquals(collection, null));
            Contract.Requires(!ReferenceEquals(selector, null));

            var min = new MinValue();
            var result = default(T);
            var I = -1;
            collection.Select((t, i) => new { t, i })
                .Where(v => min.AddValue(selector(v.t)))
                .Foreach(v => { result = v.t; I = v.i; });
            index = I;
            return result;
        }


        public static string ToSeparatedString<T>(IEnumerable<T> collection, string Separator)
        {
            return collection
                .Select((t, i) => new { v = t.ToString(), s = i == 0 ? "" : Separator })
                .Aggregate(new StringBuilder(), (sb, v) => sb.AppendFormat("{0}{1}", v.s, v.v), sb => sb.ToString());
        }

        public static List<T> ToListFast<T>(this IEnumerable<T> Enum) { return Enum is List<T> ? (List<T>)Enum : Enum.ToList(); }

        [Diagnostics.Contracts.Pure, DebuggerStepThrough]
        public static Complex Sum(this IEnumerable<Complex> collection) { return collection.Aggregate((Z, z) => Z + z); }

        ///<summary>Проверка перечисления на пустоту</summary>
        ///<param name="collection">Проверяемое перечисление</param>
        ///<typeparam name="T">Тип элемента перечисления</typeparam>
        ///<returns>Истина, если перечисление пусто</returns>
        [Obsolete("Используйте !.Any()")]
        [Diagnostics.Contracts.Pure, DebuggerStepThrough]
        public static bool IsEmpty<T>(this IEnumerable<T> collection)
        {
            Contract.Requires(!ReferenceEquals(collection, null), "Отсутствует ссылка на коллекцию");

            return !collection.Any();
        }

        /// <summary>Объединить элементы коллеции</summary>
        /// <typeparam name="T">Тип элемента коллекции</typeparam>
        /// <typeparam name="TResult">Тип результата</typeparam>
        /// <param name="collection">Исходная коллекция элементов</param>
        /// <param name="Init">Исходное состояние результата объединения</param>
        /// <param name="func">Метод объединения</param>
        /// <param name="index">Индекс элемента коллекции</param>
        /// <returns>Результат объединения коллекции элементов</returns>
        [Diagnostics.Contracts.Pure, DebuggerStepThrough]
        public static TResult Aggregate<T, TResult>(this IEnumerable<T> collection, TResult Init,
            Func<TResult, T, int, TResult> func, int index = 0)
        {
            Contract.Requires(!ReferenceEquals(collection, null), "Отсутствует ссылка на коллекцию");

            return collection.Aggregate(Init, (last, e) => func(last, e, index++));
        }

        /// <summary>Проверка на наличие элемиента в коллекции</summary>
        /// <typeparam name="T">Тип элемента</typeparam>
        /// <param name="collection">Проверяемая коллекция</param>
        /// <param name="selector">Метод выбора</param>
        /// <returns>Истина, если выполняется предикат хотя бы на одном элементе коллекции</returns>
        [Diagnostics.Contracts.Pure, DebuggerStepThrough]
        public static bool Contains<T>(this IEnumerable<T> collection, Predicate<T> selector)
        {
            Contract.Requires(!ReferenceEquals(collection, null), "Отсутствует ссылка на коллекцию");
            Contract.Requires(!ReferenceEquals(selector, null), "Отсутствует ссылка на предикат");

            if(collection is List<T>)
            {
                var list = (List<T>)collection;
                for(var i = 0; i < list.Count; i++)
                    if(selector(list[i])) return true;
            }

            if(collection is IList<T>)
            {
                var list = (IList<T>)collection;
                for(var i = 0; i < list.Count; i++)
                    if(selector(list[i])) return true;
            }

            if(collection is T[])
            {
                var list = (T[])collection;
                for(var i = 0; i < list.Length; i++)
                    if(selector(list[i])) return true;
            }

            return collection.Any(t => selector(t));
        }

        /// <summary>Найти элемент в перечислении, удовлетворяющий предикату</summary>
        /// <param name="collection">Перечисление элементов</param>
        /// <param name="selector">Предикат выбора</param>
        /// <typeparam name="T">Тип элементов перечисления</typeparam>
        /// <returns>Найденный элемент, либо пустая ссылка</returns>
        [Diagnostics.Contracts.Pure, DebuggerStepThrough]
        public static T Find<T>(this IEnumerable<T> collection, Predicate<T> selector)
        {
            Contract.Requires(!ReferenceEquals(collection, null), "Отсутствует ссылка на коллекцию");
            Contract.Requires(!ReferenceEquals(selector, null), "Отсутствует ссылка на предикат");

            foreach(var local in collection.Where(local => selector(local))) return local;
            return default(T);
        }

        ///<summary>Выполнение действия для всех элементов коллекции</summary>
        ///<param name="collection">Коллекция элементов</param>
        ///<param name="Action">Выполняемое действие</param>
        ///<typeparam name="T">Тип элементов коллекции</typeparam>
        [DebuggerStepThrough]
        public static void Foreach<T>(this IEnumerable<T> collection, Action<T> Action)
        {
            Contract.Requires(!ReferenceEquals(collection, null), "Отсутствует ссылка на коллекцию");
            Contract.Requires(!ReferenceEquals(Action, null));

            if(collection is IList<T>)
            {
                var list = (IList<T>)collection;
                for(var i = 0; i < list.Count; i++)
                    Action(list[i]);
            }
            else if(collection is IList)
            {
                var list = (IList)collection;
                for(var i = 0; i < list.Count; i++)
                    Action((T)list[i]);
            }
            else
                foreach(var local in collection)
                    Action(local);
        }

        ///<summary>Выполнение действия для всех элементов коллекции с указанием индекса элемента</summary>
        ///<param name="collection">Коллекция элементов</param>
        ///<param name="Action">Действие над элементом</param>
        ///<param name="index">Смещение индекса элемента колеекции</param>
        ///<typeparam name="T">Тип элемента колекции</typeparam>
        [DebuggerStepThrough]
        public static void Foreach<T>(this IEnumerable<T> collection, Action<T, int> Action, int index = 0)
        {
            Contract.Requires(!ReferenceEquals(collection, null), "Отсутствует ссылка на коллекцию");
            Contract.Requires(!ReferenceEquals(Action, null));

            if(collection is T[])  // Если коллекция - массив
            {
                var list = (T[])collection;
                var lv_ElementCount = list.Length;
                for(var i = 0; i < lv_ElementCount; i++)
                    Action(list[i], index++);

            }
            else if(collection is IList<T>) // Если коллекция - список
            {
                var list = (IList<T>)collection;
                var lv_ElementCount = list.Count;
                for(var i = 0; i < lv_ElementCount; i++)
                    Action(list[i], index++);
            }
            else if(collection is IList)  // Если коллекция - нетипизированный список
            {
                var list = (IList)collection;
                var lv_ElementCount = list.Count;
                for(var i = 0; i < lv_ElementCount; i++)
                    Action((T)list[i], index++);
            }
            else
                foreach(var local in collection)
                    Action(local, index++);
        }

        /// <summary>Ленивое преобразование типов, пропускающее непреобразуемые объекты</summary>
        /// <param name="collection">Исходное перечисление объектов</param>
        /// <typeparam name="T">Тип объектов входного перечисления</typeparam>
        //        /// <typeparam name="TOut">Тип объектов выходного перечисления</typeparam>
        /// <returns>Коллекция объектов преобразованного типа</returns>
        [Diagnostics.Contracts.Pure, DebuggerStepThrough]
        public static IEnumerable<T> CastLeazy<T>(IEnumerable collection)
        {
            Contract.Requires(!ReferenceEquals(collection, null), "Отсутствует ссылка на коллекцию");
            Contract.Ensures(!ReferenceEquals(Contract.Result<IEnumerable<T>>(), null));
            var result = collection as IEnumerable<T>;
            return result ?? collection.Cast<object>().Where(item => item is T).Cast<T>();
        }

        public static IEnumerable<Q> CastLeazy<T, Q>(IEnumerable<T> collection)
        {
            Contract.Requires(!ReferenceEquals(collection, null), "Отсутствует ссылка на коллекцию");
            var result = typeof(T) == typeof(Q);
            return result ? (IEnumerable<Q>)collection : collection.Where(v => v is Q).Cast<Q>();
        }

        public static IEnumerable<T> ForeachLeazyIf<T>(this IEnumerable<T> collection, Func<T, bool> Predicat,
                    Action<T> Action)
        {
            Contract.Requires(!ReferenceEquals(collection, null), "Отсутствует ссылка на коллекцию");
            Contract.Requires(!ReferenceEquals(Predicat, null));
            return Action == null ? collection : collection.Select(t =>
            {
                if(Predicat(t)) Action(t);
                return t;
            });
        }

        /// <summary>Отложенное выполнение указанного действия для каждого элемента колеекци</summary>
        /// <typeparam name="T">Тип элементов коллеции</typeparam>
        /// <param name="collection">Коллекция элементов</param>
        /// <param name="Action">Выполняемое действие</param>
        /// <returns>Коллекция элементов, для элементов которой выполняется отложенное действие</returns>
        [Diagnostics.Contracts.Pure, DebuggerStepThrough]
        public static IEnumerable<T> ForeachLeazy<T>(this IEnumerable<T> collection, Action<T> Action)
        {
            Contract.Requires(!ReferenceEquals(collection, null), "Отсутствует ссылка на коллекцию");
            Contract.Ensures(ReferenceEquals(Action, null) // Если действие не указано
                // то проверить, что входная коллекция соответствует выходной
                ? ReferenceEquals(Contract.Result<IEnumerable<T>>(), collection)
                // иначе - убедиться, что на выходе не нулевая ссылка
                : !ReferenceEquals(Contract.Result<IEnumerable<T>>(), null));

            return Action == null ? collection : collection.Select(t =>
            {
                Action(t);
                return t;
            });
        }

        public static IEnumerable<T> ForeachLeazyLast<T>(this IEnumerable<T> collection, Action<T> action)
        {
            Contract.Requires(!ReferenceEquals(collection, null), "Отсутствует ссылка на коллекцию");
            if(action == null)
                foreach(var value in collection) yield return value;
            else
                foreach(var value in collection)
                {
                    yield return value;
                    action(value);
                }
        }

        /// <summary>Отложенное выполнение действия до перебора элементов колеекци</summary>
        /// <typeparam name="T">Тип элементов коллеции</typeparam>
        /// <param name="collection">Коллекция элементов</param>
        /// <param name="Action">Выполняемое действие</param>
        /// <param name="index">Начальный индекс элемента коллекции</param>
        /// <returns>Коллекция элементов, для элементов которой которой выполняется действие</returns>
        [Diagnostics.Contracts.Pure, DebuggerStepThrough]
        public static IEnumerable<T> ForeachLeazy<T>(this IEnumerable<T> collection, Action<T, int> Action, int index = 0)
        {
            Contract.Requires(!ReferenceEquals(collection, null));
            Contract.Ensures(ReferenceEquals(Action, null)
                ? ReferenceEquals(Contract.Result<IEnumerable<T>>(), collection)
                : !ReferenceEquals(Contract.Result<IEnumerable<T>>(), null));
            return Action == null ? collection : collection.Select(t =>
            {
                Action(t, index++);
                return t;
            });
        }

        ///<summary>Вычисление функции от всех элементов колеекции</summary>
        ///<param name="Collection">Коллекция значений аргумента вычисляемой функции</param>
        ///<param name="f">Вычисляемая функция</param>
        ///<typeparam name="T">Тип аргумента функции</typeparam>
        ///<typeparam name="TResult">Тип значения функции</typeparam>
        ///<returns>Коллекция значений функции на всей области определения</returns>
        [Diagnostics.Contracts.Pure, Obsolete("Используйте .Select(Func<T, TResult>)")]
        //[Diagnostics.DebuggerStepThrough]
        public static IEnumerable<TResult> Function<T, TResult>(this IEnumerable<T> Collection, Func<T, TResult> f)
        {
            Contract.Requires(!ReferenceEquals(Collection, null));
            Contract.Requires(!ReferenceEquals(f, null));
            //            Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);
            return new EnumirableCollectionFunctionCalculator<T, TResult>(Collection, f);
        }

        ///<summary>Класс вычисления значений функции для коллекции аргументов</summary>
        ///<typeparam name="T">Тип аргумента функции</typeparam>
        ///<typeparam name="TResult">Тип значения функции</typeparam>
        private class EnumirableCollectionFunctionCalculator<T, TResult> : IEnumerable<TResult>
        {
            /* ------------------------------------------------------------------------------------------ */

            ///<summary>Перечислитель коллеции рассчитанных значений функции</summary>
            public class EnumirableCollectionFunctionCalculatorEnumirator : IEnumerator<TResult>
            {
                /* ------------------------------------------------------------------------------------------ */

                /// <summary>Перечислитель коллекции аргументов функции</summary>
                private readonly IEnumerator<T> f_Enumirator;

                /// <summary>Вычисляемая функция</summary>
                private readonly Func<T, TResult> f_Function;

                private readonly LazyValue<TResult> f_FunctionValue;

                /* ------------------------------------------------------------------------------------------ */

                /// <summary>Текцущий элемент коллекции значений функции</summary>
                public TResult Current { get { return f_FunctionValue; } }

                /// <summary>Текцущий элемент коллекции значений функции</summary>
                object IEnumerator.Current { get { return Current; } }

                /* ------------------------------------------------------------------------------------------ */

                ///<summary>Инициализация нового перечислителя колеекции рассчитанных значений функции</summary>
                ///<param name="Enumirator">Перечислитель коллекции аргументов области определения</param>
                ///<param name="f">Вычисляемая функция</param>
                public EnumirableCollectionFunctionCalculatorEnumirator(IEnumerator<T> Enumirator, Func<T, TResult> f)
                {
                    Contract.Requires(Enumirator != null);
                    Contract.Requires(!ReferenceEquals(f, null));

                    f_Enumirator = Enumirator;
                    f_Function = f;
                    f_FunctionValue = new LazyValue<TResult>(() => f_Function(f_Enumirator.Current));
                }

                /* ------------------------------------------------------------------------------------------ */

                /// <summary>Освобождение ресурсов</summary>
                public void Dispose() { f_Enumirator.Dispose(); }

                /// <summary>Переход к следующему элементу коллекции</summary>
                /// <returns>Истина, если переход выполнен успешно</returns>
                public bool MoveNext()
                {
                    var CanMove = f_Enumirator.MoveNext();
                    if(CanMove) f_FunctionValue.Reset();
                    return CanMove;
                }

                /// <summary>Сброс состояния перечислителя</summary>
                public void Reset()
                {
                    f_Enumirator.Reset();
                    f_FunctionValue.Reset();
                }

                /* ------------------------------------------------------------------------------------------ */

                [ContractInvariantMethod]
                private void Invariant()
                {
                    Contract.Invariant(f_Enumirator != null);
                    Contract.Invariant(f_Function != null);
                    Contract.Invariant(f_FunctionValue != null);
                }

                /* ------------------------------------------------------------------------------------------ */
            }

            /* ------------------------------------------------------------------------------------------ */

            /// <summary>Коллекция аргументов функции</summary>
            private readonly IEnumerable<T> f_Collection;

            /// <summary>Вычисляемая функция</summary>
            private readonly Func<T, TResult> f_Function;

            /// <summary>Инициализация нового потокового вычислителя функции на коллекции аргументов</summary>
            /// <param name="Collection">Коллекция аргументов</param>
            /// <param name="f">Вычисляемая функция</param>
            public EnumirableCollectionFunctionCalculator(IEnumerable<T> Collection, Func<T, TResult> f)
            {
                Contract.Requires(!ReferenceEquals(Collection, null));
                Contract.Requires(!ReferenceEquals(f, null));

                f_Collection = Collection;
                f_Function = f;
            }

            /// <summary>Получение перечислителя</summary>
            /// <returns>Перечислитель рассчитанных значений функции</returns>
            public IEnumerator<TResult> GetEnumerator()
            {
                Contract.Ensures(Contract.Result<IEnumerator<TResult>>() != null);
                return new EnumirableCollectionFunctionCalculatorEnumirator(f_Collection.GetEnumerator(), f_Function);
            }

            /// <summary>Неявное получение перечислителя</summary>
            /// <returns>Перечислитель расссчитанных значений функции</returns>
            IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }

            [ContractInvariantMethod]
            private void Invariant()
            {
                Contract.Invariant(f_Collection != null);
                Contract.Invariant(f_Function != null);
            }
        }

        public static IEnumerable<T> ExistingItems<T>(this IEnumerable<T> Source, IEnumerable<T> Items)
        {
            Contract.Requires(Source != null);
            Contract.Requires(Items != null);
            var list = Items.ToListFast();
            return Source.Where(t => list.Exists(i => i.Equals(t)));
        }

        public static IEnumerable<T> GetUnique<T>(this IEnumerable<T> values, Func<T, T, bool> Comparer)
        {
            Contract.Requires(values != null);
            Contract.Requires(Comparer != null);
            var list = new List<T>();
            foreach(var value in values.Where(value => !list.Exists(v => Comparer(value, v))))
            {
                list.Add(value);
                yield return value;
            }
        }

        public static IEnumerable<T> GetUnique<T>(this IEnumerable<T> values)
        {
            Contract.Requires(values != null);
            var list = new List<T>();
            return values.Where(v => !list.Contains(v)).ForeachLeazy(list.Add);
            //foreach(var value in values.Where(value => !list.Contains(value)))
            //{
            //    list.Add(value);
            //    yield return value;
            //}
        }

        public static IEnumerable<T> MisingItems<T>(this IEnumerable<T> Source, IEnumerable<T> Items)
        {
            Contract.Requires(Source != null);
            Contract.Requires(Items != null);
            var list = Items.ToListFast();
            return Source.Where(t => !list.Exists(i => i.Equals(t)));
        }

        public static T[] Intersection<T>(this IEnumerable<T> A, IEnumerable<T> B)
        {
            Contract.Requires(A != null);
            Contract.Requires(B != null);
            var a = A.ToListFast();
            var b = B.ToListFast();

            var result = new List<T>(a.Count + b.Count);
            result.AddRange(a.ExistingItems(b));
            result.AddRange(b.ExistingItems(a));

            return result.ToArray();
        }

        public static bool ItemEquals<T>(this IEnumerable<T> A, IEnumerable<T> B)
        {
            Contract.Requires(A != null);
            Contract.Requires(B != null);

            using(var enumA = A.GetEnumerator())
            using(var enumB = B.GetEnumerator())
            {
                var nextA = enumA.MoveNext();
                var nextB = enumB.MoveNext();
                while(nextA && nextB)
                {
                    if(ReferenceEquals(enumA.Current, null) && !ReferenceEquals(enumB.Current, null)) return false;
                    if(enumA.Current == null || !enumA.Current.Equals(enumB.Current)) return false;
                    nextA = enumA.MoveNext();
                    nextB = enumB.MoveNext();
                }
                return nextA == nextB;
            }
        }

        public static T[] NotIntersection<T>(this IEnumerable<T> A, IEnumerable<T> B)
        {
            Contract.Requires(A != null);
            Contract.Requires(B != null);

            var a = A.ToListFast();
            var b = B.ToListFast();

            var result = new List<T>(a.Count + b.Count);
            result.AddRange(a.MisingItems(b));
            result.AddRange(b.MisingItems(a));

            return result.ToArray();
        }

        public static void Xor<T>(this IEnumerable<T> A, IEnumerable<T> B,
            out T[] MissingInAFromB, out T[] MissingInBFromA,
            out T[] ExistingInAFromB, out T[] ExistingInBFromA,
            out T[] Intersection, out T[] NotIntersection)
        {
            Contract.Requires(A != null);
            Contract.Requires(B != null);
            Contract.Ensures(Contract.ValueAtReturn(out MissingInAFromB) != null);
            Contract.Ensures(Contract.ValueAtReturn(out MissingInBFromA) != null);
            Contract.Ensures(Contract.ValueAtReturn(out ExistingInAFromB) != null);
            Contract.Ensures(Contract.ValueAtReturn(out ExistingInBFromA) != null);
            Contract.Ensures(Contract.ValueAtReturn(out Intersection) != null);
            Contract.Ensures(Contract.ValueAtReturn(out NotIntersection) != null);


            var a = A.ToListFast();
            var b = B.ToListFast();

            var MissingInAFromB_list = new List<T>(a.Count + b.Count);
            var MissingInBFromA_list = new List<T>(a.Count + b.Count);
            var ExistingInAFromB_list = new List<T>(a.Count + b.Count);
            var ExistingInBFromA_list = new List<T>(a.Count + b.Count);
            var Intersection_list = new List<T>(a.Count + b.Count);
            var NotIntersection_list = new List<T>(a.Count + b.Count);

            var b_existing_in_a = new bool[b.Count];
            for(var i = 0; i < a.Count; i++)
            {
                var a_item = a[i];
                var a_existing_in_b = false;

                for(var j = 0; j < b.Count; j++)
                {
                    var b_item = b[j];
                    if(!a_item.Equals(b_item)) continue;

                    a_existing_in_b = b_existing_in_a[j] = true;
                    break;
                }

                if(a_existing_in_b)
                {
                    ExistingInAFromB_list.Add(a_item);
                    NotIntersection_list.Add(a_item);
                }
                else
                {
                    MissingInAFromB_list.Add(a_item);
                    Intersection_list.Add(a_item);
                }
            }

            for(var i = 0; i < b.Count; i++)
            {
                var b_item = b[i];
                if(b_existing_in_a[i])
                {
                    ExistingInBFromA_list.Add(b_item);
                    NotIntersection_list.Add(b_item);
                }
                else
                {
                    MissingInBFromA_list.Add(b_item);
                    Intersection_list.Add(b_item);
                }
            }

            ExistingInAFromB = ExistingInAFromB_list.ToArray();
            ExistingInBFromA = ExistingInBFromA_list.ToArray();
            MissingInAFromB = MissingInAFromB_list.ToArray();
            MissingInBFromA = MissingInBFromA_list.ToArray();
            Intersection = Intersection_list.ToArray();
            NotIntersection = NotIntersection_list.ToArray();
        }

        public static string ToSeparatedStr<T>(this IEnumerable<T> collection, string Separator = "")
        {
            Contract.Requires(collection != null);
            return string.Join(Separator, collection.Select(o => o.ToString()).ToArray());

            //var first = true;
            //return collection.Aggregate(
            //    new StringBuilder(),
            //    (sb, t) =>
            //    {
            //        if(first)
            //        {
            //            sb.AppendFormat("{0}", t);
            //            first = false;
            //        }
            //        else
            //            sb.AppendFormat("{0}{1}", Separator, t);
            //        return sb;
            //    },
            //    sb => sb.ToString());
        }

        public static Interval GetMinMax(this IEnumerable<double> values)
        {
            Contract.Requires(values != null);
            Contract.Ensures(Contract.Result<Interval>() != null);
            return new MinMaxValue(values).Interval;
        }

        public static IEnumerable<T> AppendLast<T>(this IEnumerable<T> collection, T obj)
        {
            Contract.Requires(collection != null);
            Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);
            foreach(var value in collection)
                yield return value;
            yield return obj;
        }

        public static IEnumerable<T> AppendFirst<T>(this IEnumerable<T> collection, T obj)
        {
            Contract.Requires(collection != null);
            Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);
            yield return obj;
            foreach(var value in collection)
                yield return value;
        }

        public static IEnumerable<T> InsertAtPos<T>(this IEnumerable<T> collection, T obj, int pos)
        {
            Contract.Requires(collection != null);
            Contract.Requires(pos >= 0);
            Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);
            var i = 0;
            foreach(var value in collection)
            {
                if(i == pos) { yield return obj; i++; }
                yield return value;
                i++;
            }
        }

        public static IEnumerable<T> ConcatInverted<T>(this IEnumerable<T> FirstCollection, IEnumerable<T> SecondCollection)
        {
            Contract.Requires(FirstCollection != null);
            Contract.Requires(SecondCollection != null);
            Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);
            foreach(var value in SecondCollection) yield return value;
            foreach(var value in FirstCollection) yield return value;
        }

        public static Polynom Sum(this IEnumerable<Polynom> P)
        {
            Polynom result = null;
            foreach(var p in P)
            {
                if(result == null) result = p;
                else
                    result += p;
            }
            return result ?? new Polynom(0);
        }

        public static Polynom Multyiply(this IEnumerable<Polynom> P)
        {
            Polynom result = null;
            foreach(var p in P)
            {
                if(result == null) result = p;
                else
                    result *= p;
            }
            return result ?? new Polynom(1);
        }
    }
}

